#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

//进程程序替换细节：
//1、程序一旦替换成功，execl后续代码不执行。因为被替换了
//2、execl只有失败才有返回值，成功没有返回值
//3、替换完成，不创建心得程序
//4、创建一个进程是先场景PCB，地址空间，页表，在是把程序加载到内存
//程序替换所做的工作：就是加载！  有替换替换，没替换加载（冯诺依曼规定） 
//
//程序替换起到一个加载器的作用
//




//补充：标准传参，也可以非标准传参
//例子：
    //execl("/usr/bin/top","top",NULL); //NULL  不是”NULL“
    //execl("/usr/bin/top","/usr/bin/top",NULL); //NULL  不是”NULL“
//这两个可以达到相同的目的



//
//int main()
//{
//    printf("I am a process,pid:%d\n",getpid());
//    printf("exec beginn...\n");
//
//
//
//    execl("/usr/bin/ls","ls","-a","-l",NULL); //NULL  不是”NULL“
//    //execl("/usr/bin/top","top",NULL); //NULL  不是”NULL“
//    printf("exce end ...\n");
//            
//    return 0;
//}



//1、为什么要子进程程序替换，父进程去等待
//因为，我们创建了父进程，父进程可以获得子进程的运行结果，从而判定执行结果如何

//2、为什么替换没有影响父进程
//因为进程具有独立性，替换时父进程会发生写时拷贝（不单单是数据发生写是拷贝，代码也会发生写时拷贝）


//写时拷贝的本质就是重新开辟空间


//3、你的shell是如何运行起来一个指令的
//   指令是个可执行程序，命令行是个bash(进程)
//   shell创建子进程，shell去waitpid等待子进程，子进程会继承你输入的shell的代码，但不影响子进程直接程序替换，替换你所输入的指令，运行完成。





int main()
{
    char*const env[]={
        (char*)"haha=hehe",
        (char*)"PATH=/"
    };

    printf("I am a process,pid:%d\n",getpid());
    //putenv("MYVAL=aaaaaaaaaaaaaaaaaaaaaaaaaa");
    pid_t id=fork();
    if(id==0)
    {
        sleep(2);
        //只要是可以在linux下运行的都可以调用，比如可以C调用C++，C++调用java,等等
        //因为所有的语言，运行之后，都是进程，exec可以替换进程
        


       // execl("./mytest","mytest",NULL);     
        //我们没有传递环境变量表，子进程就拿到了，是怎么做到了？
        //默可以通过地址空间继承方式，让所有子进程拿到环境变量
        //所以进程程序替换不会替换环境变量数据
        //
        //1、我们想让子进程继承全部环境变量们直接拿到
        //2、单纯的新增环境变量    用putenv
        //3、如果我想设置全新的环境变量
        

        execle("./mytest","mytest",NULL,environ);  //可以自定义环境变量，也可以传递父进程的环境变量表
        //
        //execle("./mytest","mytest",NULL,env);
        //


        //execl("/usr/bin/bash","bash","test.sh",NULL);
       
   //    char*argv[]={
   //             (char*)"ls",
   //             (char*)"-a",
   //             (char*)"-l"
   //     };

   //     sleep(3);
   //     printf("exec begin ...\n");


        //execvp("ls",argv); //带p就是可以不用相对路径
        //execv("/usr/bin/ls",argv);//argv[]是命令行参数的一张表,把对应的参数形成一个指针数组，传入指针数组
        //execl("/usr/bin/ls","ls","-l","-a",NULL);
        //execlp("ls","ls","-a","-l",NULL);   //可以不带相对路径，直接写出替换的指令
      
        //带p就是不用加相对路径，带v参数传的是指针数组。带e的是
        //execle()    可以传递自己设定的环境变量

        printf("exec end...\n");
        exit(1);
    }

    //father
    pid_t rid=waitpid(id,NULL,0);
    if(rid>0)//等待成功
    {
        printf("wait success\n");
    }
    exit(1);
}

//总结：exec这么多接口功能上没区别（都是程序替换），知识使用上的区别（传参不同）
//程序替换的系统调用只有一个：execve;   剩下的（execl,execlp,execle,execv,execvp,execvpe）都是系统调用execve的封装
